/*
 * @Copyright: 2016 www.yyfax.com Inc. All rights reserved.
 */
package com.jaws.core.compatible.service.impl;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.MutablePair;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.jaws.core.common.util.DateUtil;
import com.jaws.core.common.util.Md5Util;
import com.jaws.core.compatible.dao.SequenceDao;
import com.jaws.core.compatible.enums.SequenceStrategy;
import com.jaws.core.compatible.exception.YYRuntimeException;
import com.jaws.core.compatible.service.SequenceService;
import com.jaws.core.mvc.exception.RspCodeEnum;

/**
 * 业务主键生产服务
 * 
 * @author jiangmy
 * @date 2016-12-02 16:54:47
 * @since v1.0.0
 */
public class SequenceServiceImpl implements SequenceService {

	@Autowired
	private SequenceDao sequenceDao;
	//
	private AtomicInteger counter = new AtomicInteger();

	private final String split = "";

	// private static final Map<String, MutablePair<Long, Long>> _POOL = new ConcurrentHashMap<String, MutablePair<Long, Long>>();
	private static ThreadLocal<Map<String, MutablePair<Long, Long>>> _POOL = new ThreadLocal<Map<String, MutablePair<Long, Long>>>() {

		protected Map<String, MutablePair<Long, Long>> initialValue() {
			return new HashMap<String, MutablePair<Long, Long>>();
		}
	};

	private final int step = 30;

	@Override
	public String uuid32() {
		return UUID.randomUUID().toString().replace("-", "");
	}

	@Override
	public String uuid16() {
		return Md5Util.smd5(uuid32());
	}

	@Override
	public String uuidTimestamped32() {
		return Long.toHexString(System.currentTimeMillis()) + split
				+ (StringUtils.leftPad(Integer.toHexString(counter.incrementAndGet() % Integer.parseInt("FFFFF", 16)), 5, "0")) + split + uuid16();
	}

	@Override
	public String uuidSerail32() {
		return Long.toHexString(Long.valueOf(DateUtil.date2Str(new Date(), "yyMMddHHmmssSSS"))) + split
				+ (StringUtils.leftPad(Integer.toHexString(counter.incrementAndGet() % Integer.parseInt("FFFF", 16)), 4, "0")) + split + uuid16();
	}

	@Override
	public int sequenceId(String namespace) {
		return sequenceId(namespace, step);
	}

	@Override
	public int sequenceId(String namespace, int step) {
		Map<String, MutablePair<Long, Long>> map = _POOL.get();
		MutablePair<Long, Long> pair = map.get(namespace);
		if (pair == null || pair.getKey() + 1 >= pair.getValue()) {
			// 需要从数据库申请sequence段
			Long rs = sequenceDao.nextLong(namespace, step);
			if (rs < 1) {
				throw new YYRuntimeException(RspCodeEnum.COMMON_ERROR_SQL_EXCEPTION, "从数据库申请sequence号段失败! {}", namespace);
			}
			pair = new MutablePair<Long, Long>(rs, rs + step);
			map.put(namespace, pair);
		} else {
			pair.setLeft(pair.getKey() + 1);
		}
		return Long.valueOf(pair.getKey()).intValue();
	}

	@Override
	public String sequenceStr(String key, String prefix, int step, int len) {
		int leftLen = len - prefix.length();
		int next = sequenceId(key, step);
		return prefix + StringUtils.leftPad(next + "", leftLen, "0");
	}

	@Override
	public String sequenceStr(SequenceStrategy strategy) {
		return sequenceStr(strategy.getKey(), strategy.getPrefix(), strategy.getStep(), strategy.getLen());
	}
}
